home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / modes / cmacexp.el.z / cmacexp.el
Encoding:
Text File  |  1998-05-21  |  13.9 KB  |  390 lines

  1. ;;; cmacexp.el --- expand C macros in a region
  2.  
  3. ;; Copyright (C) 1992, 1994, 1996 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Francesco Potorti` <pot@cnuce.cnr.it>
  6. ;; Version: $Id: cmacexp.el,v 1.26 1996/06/07 22:59:27 rms Exp $
  7. ;; Adapted-By: ESR
  8. ;; Keywords: c
  9.  
  10. ;; This file is part of XEmacs.
  11.  
  12. ;; XEmacs is free software; you can redistribute it and/or modify it
  13. ;; under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your option)
  15. ;; any later version.
  16.  
  17. ;; XEmacs is distributed in the hope that it will be useful, but
  18. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20. ;; General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with XEmacs; see the file COPYING.  If not, write to the Free
  24. ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  25. ;; 02111-1307, USA.
  26.  
  27. ;;; Synched up with: FSF 19.34.
  28.  
  29. ;; USAGE =============================================================
  30.  
  31. ;; In C mode C-C C-e is bound to c-macro-expand.  The result of the
  32. ;; expansion is put in a separate buffer.  A user option allows the
  33. ;; window displaying the buffer to be optimally sized.
  34. ;;
  35. ;; When called with a C-u prefix, c-macro-expand replaces the selected
  36. ;; region with the expansion.  Both the preprocessor name and the
  37. ;; initial flag can be set by the user.  If c-macro-prompt-flag is set
  38. ;; to a non-nil value the user is offered to change the options to the
  39. ;; preprocessor each time c-macro-expand is invoked.  Preprocessor
  40. ;; arguments default to the last ones entered.  If c-macro-prompt-flag
  41. ;; is nil, one must use M-x set-variable to set a different value for
  42. ;; c-macro-cppflags.
  43.  
  44. ;; A c-macro-expansion function is provided for non-interactive use.
  45.  
  46. ;; INSTALLATION ======================================================
  47.  
  48. ;; Put the following in your ~/.emacs file.
  49.  
  50. ;; If you want the *Macroexpansion* window to be not higher than
  51. ;; necessary: 
  52. ;;(setq c-macro-shrink-window-flag t)
  53. ;;
  54. ;; If you use a preprocessor other than /lib/cpp (be careful to set a
  55. ;; -C option or equivalent in order to make the preprocessor not to
  56. ;; strip the comments):
  57. ;;(setq c-macro-preprocessor "gpp -C")
  58. ;;
  59. ;; If you often use a particular set of flags:
  60. ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG"
  61. ;;
  62. ;; If you want the "Preprocessor arguments: " prompt:
  63. ;;(setq c-macro-prompt-flag t)
  64.  
  65. ;; BUG REPORTS =======================================================
  66.  
  67. ;; Please report bugs, suggestions, complaints and so on to
  68. ;; pot@cnuce.cnr.it (Francesco Potorti`).
  69.  
  70. ;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ==========================
  71.  
  72. ;; - A lot of user and programmer visible changes.  See above.
  73. ;; - #line directives are inserted, so __LINE__ and __FILE__ are
  74. ;;   correctly expanded.  Works even with START inside a string, a
  75. ;;   comment or a region #ifdef'd away by cpp. cpp is invoked with -C,
  76. ;;   making comments visible in the expansion.
  77. ;; - All work is done in core memory, no need for temporary files.
  78.  
  79. ;; ACKNOWLEDGEMENTS ==================================================
  80.  
  81. ;; A lot of thanks to Don Maszle who did a great work of testing, bug
  82. ;; reporting and suggestion of new features.  This work has been
  83. ;; partially inspired by Don Maszle and Jonathan Segal's.
  84.  
  85. ;; BUGS ==============================================================
  86.  
  87. ;; If the start point of the region is inside a macro definition the
  88. ;; macro expansion is often inaccurate.
  89.  
  90.  
  91. (require 'cc-mode)
  92.  
  93. (provide 'cmacexp)
  94.  
  95. (defgroup c-macro nil
  96.   "Expand C macros in a region."
  97.   :group 'c)
  98.  
  99.  
  100. (defcustom c-macro-shrink-window-flag nil
  101.   "*Non-nil means shrink the *Macroexpansion* window to fit its contents."
  102.   :type 'boolean
  103.   :group 'c-macro)
  104.  
  105. (defcustom c-macro-prompt-flag nil
  106.   "*Non-nil makes `c-macro-expand' prompt for preprocessor arguments."
  107.   :type 'boolean
  108.   :group 'c-macro)
  109.  
  110. (defcustom c-macro-preprocessor
  111.   ;; Cannot rely on standard directory on MS-DOS to find CPP.
  112.   (cond ((eq system-type 'ms-dos) "cpp -C")
  113.     ;; Solaris has it in an unusual place.
  114.     ((and (string-match "^[^-]*-[^-]*-\\(solaris\\|sunos5\\)"
  115.                 system-configuration)
  116.           (file-exists-p "/opt/SUNWspro/SC3.0.1/bin/acomp"))
  117.      "/opt/SUNWspro/SC3.0.1/bin/acomp -C -E")
  118.     (t "/lib/cpp -C"))
  119.   "The preprocessor used by the cmacexp package.
  120.  
  121. If you change this, be sure to preserve the `-C' (don't strip comments)
  122. option, or to set an equivalent one."
  123.   :type 'string
  124.   :group 'c-macro)
  125.  
  126. (defcustom c-macro-cppflags ""
  127.   "*Preprocessor flags used by `c-macro-expand'."
  128.   :type 'string
  129.   :group 'c-macro)
  130.  
  131. (defconst c-macro-buffer-name "*Macroexpansion*")
  132.  
  133. ;; Autoload for XEmacs
  134. ;;;###autoload
  135. (defun c-macro-expand (start end subst)
  136.   "Expand C macros in the region, using the C preprocessor.
  137. Normally display output in temp buffer, but
  138. prefix arg means replace the region with it.
  139.  
  140. `c-macro-preprocessor' specifies the preprocessor to use.
  141. Prompt for arguments to the preprocessor \(e.g. `-DDEBUG -I ./include')
  142. if the user option `c-macro-prompt-flag' is non-nil.
  143.  
  144. Noninteractive args are START, END, SUBST.
  145. For use inside Lisp programs, see also `c-macro-expansion'."
  146.  
  147.   (interactive "r\nP")
  148.   (let ((inbuf (current-buffer))
  149.     (displaybuf (if subst
  150.             (get-buffer c-macro-buffer-name)
  151.               (get-buffer-create c-macro-buffer-name)))
  152.     (expansion ""))
  153.     ;; Build the command string.
  154.     (if c-macro-prompt-flag
  155.     (setq c-macro-cppflags
  156.           (read-string "Preprocessor arguments: "
  157.                c-macro-cppflags)))
  158.     ;; Decide where to display output.
  159.     (if (and subst
  160.          (and buffer-read-only (not inhibit-read-only))
  161.          (not (eq inbuf displaybuf)))
  162.     (progn
  163.       (message
  164.        "Buffer is read only: displaying expansion in alternate window")
  165.       (sit-for 2)
  166.       (setq subst nil)
  167.       (or displaybuf
  168.           (setq displaybuf (get-buffer-create c-macro-buffer-name)))))
  169.     ;; Expand the macro and output it.
  170.     (setq expansion (c-macro-expansion start end
  171.                        (concat c-macro-preprocessor " "
  172.                            c-macro-cppflags) t))
  173.     (if subst
  174.     (let ((exchange (= (point) start)))
  175.       (delete-region start end)
  176.       (insert expansion)
  177.       (if exchange
  178.           (exchange-point-and-mark)))
  179.       (set-buffer displaybuf)
  180.       (setq buffer-read-only nil)
  181.       (buffer-disable-undo displaybuf)
  182.       (erase-buffer)
  183.       (insert expansion)
  184.       (set-buffer-modified-p nil)
  185.       (if (string= "" expansion)
  186.       (message "Null expansion")
  187.     (c-macro-display-buffer))
  188.       (setq buffer-read-only t)
  189.       (setq buffer-auto-save-file-name nil)
  190.       (bury-buffer displaybuf))))
  191.  
  192.  
  193. ;; Display the current buffer in a window which is either just large
  194. ;; enough to contain the entire buffer, or half the size of the
  195. ;; screen, whichever is smaller.  Do not select the new
  196. ;; window.
  197. ;;
  198. ;; Several factors influence window resizing so that the window is
  199. ;; sized optimally if it is created anew, and so that it is messed
  200. ;; with minimally if it has been created by the user.  If the window
  201. ;; chosen for display exists already but contains something else, the
  202. ;; window is not re-sized.  If the window already contains the current
  203. ;; buffer, it is never shrunk, but possibly expanded.  Finally, if the
  204. ;; variable c-macro-shrink-window-flag is nil the window size is *never*
  205. ;; changed.
  206. (defun c-macro-display-buffer ()
  207.   (goto-char (point-min))
  208.   (c-mode)
  209.   (let ((oldwinheight (window-height))
  210.     (alreadythere            ;the window was already there
  211.      (get-buffer-window (current-buffer)))
  212.     (popped nil))            ;the window popped changing the layout 
  213.     (or alreadythere
  214.     (progn
  215.       (display-buffer (current-buffer) t)
  216.       (setq popped (/= oldwinheight (window-height)))))
  217.     (if (and c-macro-shrink-window-flag    ;user wants fancy shrinking :\)
  218.          (or alreadythere popped))
  219.     ;; Enlarge up to half screen, or shrink properly.
  220.     (let ((oldwin (selected-window))
  221.           (minheight 0)
  222.           (maxheight 0))
  223.       (save-excursion
  224.         (select-window (get-buffer-window (current-buffer)))
  225.         (setq minheight (if alreadythere
  226.                 (window-height)
  227.                   window-min-height))
  228.         ;; XEmacs change
  229.         (setq maxheight (/ (screen-height) 2))
  230.         (enlarge-window (- (min maxheight
  231.                     (max minheight
  232.                      (+ 2 (vertical-motion (point-max)))))
  233.                    (window-height)))
  234.         (goto-char (point-min))
  235.         (select-window oldwin))))))
  236.  
  237.  
  238. (defun c-macro-expansion (start end cppcommand &optional display)
  239.   "Run a preprocessor on region and return the output as a string.
  240. Expand the region between START and END in the current buffer using
  241. the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\").
  242. Be sure to use a -C (don't strip comments) or equivalent option.
  243. Optional arg DISPLAY non-nil means show messages in the echo area."
  244.  
  245. ;; Copy the current buffer's contents to a temporary hidden buffer.
  246. ;; Delete from END to end of buffer.  Insert a preprocessor #line
  247. ;; directive at START and after each #endif following START that are
  248. ;; not inside a comment or a string.  Put all the strings thus
  249. ;; inserted (without the "line" substring) in a list named linelist.
  250. ;; If START is inside a comment, prepend "*/" and append "/*" to the
  251. ;; #line directive.  If inside a string, prepend and append "\"".
  252. ;; Preprocess the buffer contents, then look for all the lines stored
  253. ;; in linelist starting from end of buffer.  The last line so found is
  254. ;; where START was, so return the substring from point to end of
  255. ;; buffer. 
  256.   (let ((inbuf (current-buffer))
  257.     (outbuf (get-buffer-create " *C Macro Expansion*"))
  258.     (filename (if (and buffer-file-name
  259.                (string-match (regexp-quote default-directory)
  260.                      buffer-file-name))
  261.               (substring buffer-file-name (match-end 0))
  262.             (buffer-name)))
  263.     (mymsg (format "Invoking %s%s%s on region..."
  264.                c-macro-preprocessor
  265.                (if (string= "" c-macro-cppflags) "" " ")
  266.                c-macro-cppflags))
  267.     (uniquestring "??? !!! ??? start of c-macro expansion ??? !!! ???")
  268.     (startlinenum 0)
  269.     (linenum 0)
  270.     (startstat ())
  271.     (startmarker "")
  272.     (exit-status 0)
  273.     (tempname (make-temp-name (concat
  274.                    (or (getenv "TMPDIR") (getenv "TEMP")
  275.                        (getenv "TMP") "/tmp")
  276.                    "/"))))
  277.     (unwind-protect
  278.     (save-excursion
  279.       (save-restriction
  280.         (widen)
  281.             (let ((in-syntax-table (syntax-table)))
  282.               (set-buffer outbuf)
  283.               (setq buffer-read-only nil)
  284.               (erase-buffer)
  285.               (set-syntax-table in-syntax-table))
  286.         (insert-buffer-substring inbuf 1 end))
  287.  
  288.       ;; We have copied inbuf to outbuf.  Point is at end of
  289.       ;; outbuf.  Inset a newline at the end, so cpp can correctly
  290.       ;; parse a token ending at END.
  291.           (insert "\n")
  292.  
  293.       ;; Save sexp status and line number at START.
  294.       (setq startstat (parse-partial-sexp 1 start))
  295.       (setq startlinenum (+ (count-lines 1 (point))
  296.                 (if (bolp) 1 0)))
  297.  
  298.       ;; Now we insert the #line directives after all #endif or
  299.       ;; #else following START going backward, so the lines we
  300.       ;; insert don't change the line numbers.
  301.       ;(switch-to-buffer outbuf) (debug)    ;debugging instructions
  302.       (goto-char (point-max))
  303.       (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move)
  304.         (if (equal (nthcdr 3 (parse-partial-sexp start (point)
  305.                              nil nil startstat))
  306.                '(nil nil nil 0 nil)) ;neither in string nor in
  307.                          ;comment nor after quote
  308.         (progn
  309.           (goto-char (match-end 0))
  310.           (setq linenum (+ startlinenum
  311.                    (count-lines start (point))))
  312.           (insert (format "\n#line %d \"%s\"\n" linenum filename))
  313.           (goto-char (match-beginning 0)))))
  314.  
  315.       ;; Now we are at START.  Insert the first #line directive.
  316.       ;; This must work even inside a string or comment, or after a
  317.       ;; quote.
  318.       (let* ((startinstring (nth 3 startstat))
  319.          (startincomment (nth 4 startstat))
  320.          (startafterquote (nth 5 startstat))
  321.          (startinbcomment (nth 7 startstat)))
  322.         (insert (if startafterquote " " "")
  323.             (cond (startinstring
  324.                (char-to-string startinstring))
  325.               (startincomment "*/")
  326.               (""))
  327.             (setq startmarker
  328.               (concat "\n" uniquestring
  329.                   (cond (startinstring
  330.                      (char-to-string startinstring))
  331.                     (startincomment "/*")
  332.                     (startinbcomment "//"))
  333.                   (if startafterquote "\\")))
  334.             (format "\n#line %d \"%s\"\n" startlinenum filename)))
  335.  
  336.       ;; Call the preprocessor.
  337.       (if display (message mymsg))
  338.       (setq exit-status
  339.         (call-process-region 1 (point-max)
  340.                      shell-file-name
  341.                      t (list t tempname) nil "-c"
  342.                      cppcommand))
  343.       (if display (message (concat mymsg "done")))
  344.       (if (= (buffer-size) 0)
  345.           ;; Empty output is normal after a fatal error.
  346.           (insert "\nPreprocessor produced no output\n")
  347.         ;; Find and delete the mark of the start of the expansion.
  348.         ;; Look for `# nn "file.c"' lines and delete them.
  349.         (goto-char (point-min))
  350.         (search-forward startmarker)
  351.         (delete-region 1 (point)))
  352.       (while (re-search-forward (concat "^# [0-9]+ \""
  353.                         (regexp-quote filename)
  354.                         "\"") nil t)
  355.         (beginning-of-line)
  356.         (let ((beg (point)))
  357.           (forward-line 1)
  358.           (delete-region beg (point))))
  359.  
  360.       ;; If CPP got errors, show them at the beginning.
  361.       ;; MS-DOS shells don't return the exit code of their children.
  362.       ;; Look at the size of the error message file instead, but
  363.       ;; don't punish those MS-DOS users who have a shell that does
  364.       ;; return an error code.
  365.       (or (and (or (not (boundp 'msdos-shells))
  366.                (not (member (file-name-nondirectory shell-file-name)
  367.                     msdos-shells)))
  368.            (eq exit-status 0))
  369.           (zerop (nth 7 (file-attributes (expand-file-name tempname))))
  370.           (progn
  371.         (goto-char (point-min))
  372.         ;; Put the messages inside a comment, so they won't get in
  373.         ;; the way of font-lock, highlighting etc.
  374.         (insert
  375.          (format "/* Preprocessor terminated with status %s\n\n   Messages from `%s\':\n\n"
  376.              exit-status cppcommand))
  377.         (goto-char (+ (point)
  378.                   (nth 1 (insert-file-contents tempname))))
  379.         (insert "\n\n*/\n")))
  380.       (delete-file tempname)
  381.  
  382.       ;; Compute the return value, keeping in account the space
  383.       ;; inserted at the end of the buffer.
  384.       (buffer-substring 1 (max 1 (- (point-max) 1))))
  385.  
  386.       ;; Cleanup.
  387.       (kill-buffer outbuf))))
  388.  
  389. ;;; cmacexp.el ends here
  390.